﻿namespace Jhong.StaticFile
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Threading.Tasks;

    internal class StaticFileHandler
    {
        public static IList<string> StaticPath { get; private set; }

        private static char[] _trimStart = new char[] { '/', '\\' };

        internal static StaticFileOption Option = new StaticFileOption() { CaseSensitive = true };

        static StaticFileHandler()
        {
            StaticPath = new List<string>();
            _complete.SetResult(1);
        }

        internal static void AddPath(string[] dof)
        {
            foreach (var p in dof)
            {
                var pPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, p.TrimStart(_trimStart));
                if (Directory.Exists(pPath)) StaticPath.Add((p.StartsWith("/") ? p : "/" + p.TrimStart(_trimStart)).TrimEnd(new char[] { '/', '\\' }) + "/");
                else if (File.Exists(pPath)) StaticPath.Add(p.StartsWith("/") ? p : "/" + p.TrimStart(_trimStart));
            }
        }

        internal static Task Hand(IDictionary<string, object> env)
        {
            var reqPath = env["owin.RequestPath"] as string;
            if (CheckPression(reqPath))
            {
                var targetPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, reqPath.TrimStart(_trimStart));
                if (File.Exists(targetPath)) return OutPutFile(targetPath, env);
                else return Return404(env);
            }
            return null;
        }

        private static Task Return404(IDictionary<string, object> env)
        {
            env["owin.ResponseStatusCode"] = 404;
            return _complete.Task;
        }

        private static TaskCompletionSource<int> _complete = new TaskCompletionSource<int>();

        private static bool CheckPression(string reqPath)
        {
            bool hasPression = false;
            foreach (var p in StaticPath)
            {
                if (reqPath.IndexOf(p, Option.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    hasPression = true;
                    break;
                }
            }
            return hasPression;
        }

        private static Task OutPutFile(string filePath, IDictionary<string, object> env)
        {
            var mime = MIMEMapper.GetMime(filePath);
            var length = new FileInfo(filePath).Length;
            var repHeader = env["owin.ResponseHeaders"] as IDictionary<string, string[]>;
            repHeader["Content-Type"] = new string[] { mime };
            repHeader["Content-Length"] = new string[] { length.ToString() };
            var fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read);
            return fileStream.CopyToAsync(env["owin.ResponseBody"] as Stream).ContinueWith((t) => { fileStream.Close(); });
        }
    }
}